import queueWatcher from "./asyncUpdateQueue.js";
import Dep, { popTarget, pushTarget } from "./dep.js";

let uid = 0;
class Watcher {
  // cb 是修改指定dom的闭包回调,只修改某一个
  constructor(cb, options = {}, vm = null) {
    // 序号 异步有序执行时需要 Uid 不能重复
    this._Uid = uid++;
    // 当在视图层面调用 new Wathcer 时, 把当前的节点赋值给 target
    this._cb = cb;
    this.vm = vm;
    // 记录配置项
    this.options = options;
    // dirty 计算属性实现缓存的本质 状态 true 未缓存 false 已缓存
    this.dirty = !options.dirty;
    // 记录cb的执行结果
    this.value = null;
    // 如果非懒执行, 则直接执行cb函数，cb函数执行的过程会触发 vm.xx 的属性读取行为
    if (!options.lazy) {
      this.get();
    }
  }
}

/**
 * GET负责执行 Watcher 的 cb 函数
 * 负责执行时的依赖收集
 */
Watcher.prototype.get = function () {
  // debugger;
  // console.log("push start", this._Uid);
  pushTarget(this);
  // Dep.target = this;
  this.value = this._cb.apply(this.vm);
  // console.log("push end", this._Uid);
  // Dep.target = null;
  popTarget();
};

/**
 * 响应式数据更新时，dep通知watcher执行update
 * 让update 方法执行 this._cb 函数 进而更新 DOM
 */
Watcher.prototype.update = function () {
  // 如果是计算属性 lazy 为true表示懒执行也就是计算属性
  if (this.options.lazy) {
    // 将 dirty 置为 true, 当组件更新时，重新执行 updateComponent 方法，进而执行 render 函数
    // 生成新的 vnode， patch 更新阶段将 vnode 变成真实的DOM 节点
    // 发生了 this.getAge 的属性读取操作，从而触发 get， 这是 watcher.dirty为true
    // 所以会去重新执行 evalute 方法 计算新的 computed 的值
    // 执行结束 将dirty 置为false，本次刷新周期内不会重复执行
    this.dirty = true;
  } else {
    // 加入异步队列
    // 将当前watcher放入到watcher异步更新队列 按次更新
    queueWatcher(this);
  }
};

// 由刷新watcher队列函数调用,用于刷新watcher队列
Watcher.prototype.run = function () {
  this.get();
};

/**
 * 执行 computed 计算属性的回调函数并缓存
 */
Watcher.prototype.evalute = function () {
  // 出发计算函数 cb
  this.get();
  // 状态改为已缓存 实现一次刷新周期内 computed 只执行一次
  this.dirty = false;
};

export default Watcher;
